home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 10868 / 10868.xpi / modules / util.js < prev   
Text File  |  2010-02-02  |  26KB  |  825 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Bookmarks Sync.
  15.  *
  16.  * The Initial Developer of the Original Code is Mozilla.
  17.  * Portions created by the Initial Developer are Copyright (C) 2007
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *  Dan Mills <thunder@mozilla.com>
  22.  *
  23.  * Alternatively, the contents of this file may be used under the terms of
  24.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  25.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  26.  * in which case the provisions of the GPL or the LGPL are applicable instead
  27.  * of those above. If you wish to allow use of your version of this file only
  28.  * under the terms of either the GPL or the LGPL, and not to allow others to
  29.  * use your version of this file under the terms of the MPL, indicate your
  30.  * decision by deleting the provisions above and replace them with the notice
  31.  * and other provisions required by the GPL or the LGPL. If you do not delete
  32.  * the provisions above, a recipient may use your version of this file under
  33.  * the terms of any one of the MPL, the GPL or the LGPL.
  34.  *
  35.  * ***** END LICENSE BLOCK ***** */
  36.  
  37. const EXPORTED_SYMBOLS = ['Utils', 'Svc', 'Str'];
  38.  
  39. const Cc = Components.classes;
  40. const Ci = Components.interfaces;
  41. const Cr = Components.results;
  42. const Cu = Components.utils;
  43.  
  44. Cu.import("resource://weave/ext/Preferences.js");
  45. Cu.import("resource://weave/ext/Observers.js");
  46. Cu.import("resource://weave/ext/StringBundle.js");
  47. Cu.import("resource://weave/constants.js");
  48. Cu.import("resource://weave/log4moz.js");
  49.  
  50. /*
  51.  * Utility functions
  52.  */
  53.  
  54. let Utils = {
  55.   /**
  56.    * Wrap a function to catch all exceptions and log them
  57.    *
  58.    * @usage MyObj._catch = Utils.catch;
  59.    *        MyObj.foo = function() { this._catch(func)(); }
  60.    */
  61.   catch: function Utils_catch(func) {
  62.     let thisArg = this;
  63.     return function WrappedCatch() {
  64.       try {
  65.         return func.call(thisArg);
  66.       }
  67.       catch(ex) {
  68.         thisArg._log.debug("Exception: " + Utils.exceptionStr(ex));
  69.       }
  70.     };
  71.   },
  72.  
  73.   /**
  74.    * Wrap a function to call lock before calling the function then unlock.
  75.    *
  76.    * @usage MyObj._lock = Utils.lock;
  77.    *        MyObj.foo = function() { this._lock(func)(); }
  78.    */
  79.   lock: function Utils_lock(func) {
  80.     let thisArg = this;
  81.     return function WrappedLock() {
  82.       if (!thisArg.lock())
  83.         throw "Could not acquire lock";
  84.  
  85.       try {
  86.         return func.call(thisArg);
  87.       }
  88.       finally {
  89.         thisArg.unlock();
  90.       }
  91.     };
  92.   },
  93.  
  94.   /**
  95.    * Wrap functions to notify when it starts and finishes executing or if it got
  96.    * an error. The message is a combination of a provided prefix and local name
  97.    * with the current state and the subject is the provided subject.
  98.    *
  99.    * @usage function MyObj() { this._notify = Utils.notify("prefix:"); }
  100.    *        MyObj.foo = function() { this._notify(name, subject, func)(); }
  101.    */
  102.   notify: function Utils_notify(prefix) {
  103.     return function NotifyMaker(name, subject, func) {
  104.       let thisArg = this;
  105.       let notify = function(state) {
  106.         let mesg = prefix + name + ":" + state;
  107.         thisArg._log.trace("Event: " + mesg);
  108.         Observers.notify(mesg, subject);
  109.       };
  110.  
  111.       return function WrappedNotify() {
  112.         try {
  113.           notify("start");
  114.           let ret = func.call(thisArg);
  115.           notify("finish");
  116.           return ret;
  117.         }
  118.         catch(ex) {
  119.           notify("error");
  120.           throw ex;
  121.         }
  122.       };
  123.     };
  124.   },
  125.  
  126.   batchSync: function batchSync(service, engineType) {
  127.     return function batchedSync() {
  128.       let engine = this;
  129.       let batchEx = null;
  130.  
  131.       // Try running sync in batch mode
  132.       Svc[service].runInBatchMode({
  133.         runBatched: function wrappedSync() {
  134.           try {
  135.             engineType.prototype._sync.call(engine);
  136.           }
  137.           catch(ex) {
  138.             batchEx = ex;
  139.           }
  140.         }
  141.       }, null);
  142.  
  143.       // Expose the exception if something inside the batch failed
  144.       if (batchEx!= null)
  145.         throw batchEx;
  146.     };
  147.   },
  148.  
  149.   // Generates a brand-new globally unique identifier (GUID).
  150.   makeGUID: function makeGUID() {
  151.     // 70 characters that are not-escaped URL-friendly
  152.     const code =
  153.       "!()*-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~";
  154.  
  155.     let guid = "";
  156.     let num = 0;
  157.     let val;
  158.  
  159.     // Generate ten 70-value characters for a 70^10 (~61.29-bit) GUID
  160.     for (let i = 0; i < 10; i++) {
  161.       // Refresh the number source after using it a few times
  162.       if (i == 0 || i == 5)
  163.         num = Math.random();
  164.  
  165.       // Figure out which code to use for the next GUID character
  166.       num *= 70;
  167.       val = Math.floor(num);
  168.       guid += code[val];
  169.       num -= val;
  170.     }
  171.  
  172.     return guid;
  173.   },
  174.  
  175.   anno: function anno(id, anno, val, expire) {
  176.     // Figure out if we have a bookmark or page
  177.     let annoFunc = (typeof id == "number" ? "Item" : "Page") + "Annotation";
  178.  
  179.     // Convert to a nsIURI if necessary
  180.     if (typeof id == "string")
  181.       id = Utils.makeURI(id);
  182.  
  183.     if (id == null)
  184.       throw "Null id for anno! (invalid uri)";
  185.  
  186.     switch (arguments.length) {
  187.       case 2:
  188.         // Get the annotation with 2 args
  189.         return Svc.Annos["get" + annoFunc](id, anno);
  190.       case 3:
  191.         expire = "NEVER";
  192.         // Fallthrough!
  193.       case 4:
  194.         // Convert to actual EXPIRE value
  195.         expire = Svc.Annos["EXPIRE_" + expire];
  196.  
  197.         // Set the annotation with 3 or 4 args
  198.         return Svc.Annos["set" + annoFunc](id, anno, val, 0, expire);
  199.     }
  200.   },
  201.  
  202.   ensureOneOpen: let (windows = {}) function ensureOneOpen(window) {
  203.     // Close the other window if it exists
  204.     let url = window.location.href;
  205.     let other = windows[url];
  206.     if (other != null)
  207.       other.close();
  208.  
  209.     // Save the new window for future closure
  210.     windows[url] = window;
  211.  
  212.     // Actively clean up when the window is closed
  213.     window.addEventListener("unload", function() windows[url] = null, false);
  214.   },
  215.  
  216.   // Returns a nsILocalFile representing a file relative to the
  217.   // current user's profile directory.  If the argument is a string,
  218.   // it should be a string with unix-style slashes for directory names
  219.   // (these slashes are automatically converted to platform-specific
  220.   // path separators).
  221.   //
  222.   // Alternatively, if the argument is an object, it should contain
  223.   // the following attributes:
  224.   //
  225.   //   path: the path to the file, relative to the current user's
  226.   //   profile dir.
  227.   //
  228.   //   autoCreate: whether or not the file should be created if it
  229.   //   doesn't already exist.
  230.   getProfileFile: function getProfileFile(arg) {
  231.     if (typeof arg == "string")
  232.       arg = {path: arg};
  233.  
  234.     let pathParts = arg.path.split("/");
  235.     let file = Svc.Directory.get("ProfD", Ci.nsIFile);
  236.     file.QueryInterface(Ci.nsILocalFile);
  237.     for (let i = 0; i < pathParts.length; i++)
  238.       file.append(pathParts[i]);
  239.     if (arg.autoCreate && !file.exists())
  240.       file.create(file.NORMAL_FILE_TYPE, PERMS_FILE);
  241.     return file;
  242.   },
  243.  
  244.   /**
  245.    * Add a simple getter/setter to an object that defers access of a property
  246.    * to an inner property.
  247.    *
  248.    * @param obj
  249.    *        Object to add properties to defer in its prototype
  250.    * @param defer
  251.    *        Hash property of obj to defer to (dot split each level)
  252.    * @param prop
  253.    *        Property name to defer (or an array of property names)
  254.    */
  255.   deferGetSet: function Utils_deferGetSet(obj, defer, prop) {
  256.     if (Utils.isArray(prop))
  257.       return prop.map(function(prop) Utils.deferGetSet(obj, defer, prop));
  258.  
  259.     // Split the defer into each dot part for each level to dereference
  260.     let parts = defer.split(".");
  261.     let deref = function(base) Utils.deref(base, parts);
  262.  
  263.     let prot = obj.prototype;
  264.  
  265.     // Create a getter if it doesn't exist yet
  266.     if (!prot.__lookupGetter__(prop))
  267.       prot.__defineGetter__(prop, function() deref(this)[prop]);
  268.  
  269.     // Create a setter if it doesn't exist yet
  270.     if (!prot.__lookupSetter__(prop))
  271.       prot.__defineSetter__(prop, function(val) deref(this)[prop] = val);
  272.   },
  273.  
  274.   /**
  275.    * Dereference an array of properties starting from a base object
  276.    *
  277.    * @param base
  278.    *        Base object to start dereferencing
  279.    * @param props
  280.    *        Array of properties to dereference (one for each level)
  281.    */
  282.   deref: function Utils_deref(base, props) props.reduce(function(curr, prop)
  283.     curr[prop], base),
  284.  
  285.   /**
  286.    * Determine if some value is an array
  287.    *
  288.    * @param val
  289.    *        Value to check (can be null, undefined, etc.)
  290.    * @return True if it's an array; false otherwise
  291.    */
  292.   isArray: function Utils_isArray(val) val != null && typeof val == "object" &&
  293.     val.constructor.name == "Array",
  294.  
  295.   // lazy load objects from a constructor on first access.  It will
  296.   // work with the global object ('this' in the global context).
  297.   lazy: function Weave_lazy(dest, prop, ctr) {
  298.     delete dest[prop];
  299.     dest.__defineGetter__(prop, Utils.lazyCb(dest, prop, ctr));
  300.   },
  301.   lazyCb: function Weave_lazyCb(dest, prop, ctr) {
  302.     return function() {
  303.       delete dest[prop];
  304.       dest[prop] = new ctr();
  305.       return dest[prop];
  306.     };
  307.   },
  308.  
  309.   // like lazy, but rather than new'ing the 3rd arg we use its return value
  310.   lazy2: function Weave_lazy2(dest, prop, fn) {
  311.     delete dest[prop];
  312.     dest.__defineGetter__(prop, Utils.lazyCb2(dest, prop, fn));
  313.   },
  314.   lazyCb2: function Weave_lazyCb2(dest, prop, fn) {
  315.     return function() {
  316.       delete dest[prop];
  317.       return dest[prop] = fn();
  318.     };
  319.   },
  320.  
  321.   lazySvc: function Weave_lazySvc(dest, prop, cid, iface) {
  322.     let getter = function() {
  323.       delete dest[prop];
  324.       let svc = null;
  325.  
  326.       // Try creating a fake service if we can handle that
  327.       if (!Cc[cid]) {
  328.         switch (cid) {
  329.           case "@mozilla.org/privatebrowsing;1":
  330.             svc = {
  331.               autoStarted: false,
  332.               privateBrowsingEnabled: false
  333.             };
  334.             break;
  335.         }
  336.  
  337.         let log = Log4Moz.repository.getLogger("Service.Util");
  338.         if (svc == null)
  339.           log.warn("Component " + cid + " doesn't exist on this platform.");
  340.         else
  341.           log.debug("Using a fake svc object for " + cid);
  342.       }
  343.       else
  344.         svc = Cc[cid].getService(iface);
  345.       
  346.       return dest[prop] = svc;
  347.     };
  348.     dest.__defineGetter__(prop, getter);
  349.   },
  350.  
  351.   lazyInstance: function Weave_lazyInstance(dest, prop, cid, iface) {
  352.     let getter = function() {
  353.       delete dest[prop];
  354.       dest[prop] = Cc[cid].createInstance(iface);
  355.       return dest[prop];
  356.     };
  357.     dest.__defineGetter__(prop, getter);
  358.   },
  359.  
  360.   lazyStrings: function Weave_lazyStrings(name) {
  361.     let bundle = "chrome://weave/locale/" + name + ".properties";
  362.     return function() new StringBundle(bundle);
  363.   },
  364.  
  365.   deepEquals: function eq(a, b) {
  366.     // If they're triple equals, then it must be equals!
  367.     if (a === b)
  368.       return true;
  369.  
  370.     // If they weren't equal, they must be objects to be different
  371.     if (typeof a != "object" || typeof b != "object")
  372.       return false;
  373.  
  374.     // But null objects won't have properties to compare
  375.     if (a === null || b === null)
  376.       return false;
  377.  
  378.     // Make sure all of a's keys have a matching value in b
  379.     for (let k in a)
  380.       if (!eq(a[k], b[k]))
  381.         return false;
  382.  
  383.     // Do the same for b's keys but skip those that we already checked
  384.     for (let k in b)
  385.       if (!(k in a) && !eq(a[k], b[k]))
  386.         return false;
  387.  
  388.     return true;
  389.   },
  390.  
  391.   deepCopy: function Weave_deepCopy(thing, noSort) {
  392.     if (typeof(thing) != "object" || thing == null)
  393.       return thing;
  394.     let ret;
  395.  
  396.     if (Utils.isArray(thing)) {
  397.       ret = [];
  398.       for (let i = 0; i < thing.length; i++)
  399.         ret.push(Utils.deepCopy(thing[i], noSort));
  400.  
  401.     } else {
  402.       ret = {};
  403.       let props = [p for (p in thing)];
  404.       if (!noSort)
  405.         props = props.sort();
  406.       props.forEach(function(k) ret[k] = Utils.deepCopy(thing[k], noSort));
  407.     }
  408.  
  409.     return ret;
  410.   },
  411.  
  412.   // Works on frames or exceptions, munges file:// URIs to shorten the paths
  413.   // FIXME: filename munging is sort of hackish, might be confusing if
  414.   // there are multiple extensions with similar filenames
  415.   formatFrame: function Utils_formatFrame(frame) {
  416.     let tmp = "<file:unknown>";
  417.  
  418.     let file = frame.filename || frame.fileName;
  419.     if (file)
  420.       tmp = file.replace(/^(?:chrome|file):.*?([^\/\.]+\.\w+)$/, "$1");
  421.  
  422.     if (frame.lineNumber)
  423.       tmp += ":" + frame.lineNumber;
  424.     if (frame.name)
  425.       tmp = frame.name + "()@" + tmp;
  426.  
  427.     return tmp;
  428.   },
  429.  
  430.   exceptionStr: function Weave_exceptionStr(e) {
  431.     let message = e.message ? e.message : e;
  432.     return message + " " + Utils.stackTrace(e);
  433.  },
  434.  
  435.   stackTraceFromFrame: function Weave_stackTraceFromFrame(frame) {
  436.     let output = [];
  437.     while (frame) {
  438.       let str = Utils.formatFrame(frame);
  439.       if (str)
  440.         output.push(str);
  441.       frame = frame.caller;
  442.     }
  443.     return output.join(" < ");
  444.   },
  445.  
  446.   stackTrace: function Weave_stackTrace(e) {
  447.     // Wrapped nsIException
  448.     if (e.location)
  449.       return "Stack trace: " + Utils.stackTraceFromFrame(e.location);
  450.  
  451.     // Standard JS exception
  452.     if (e.stack)
  453.       return "JS Stack trace: " + e.stack.trim().replace(/\n/g, " < ").
  454.         replace(/@(?:chrome|file):.*?([^\/\.]+\.\w+:)/g, "@$1");
  455.  
  456.     return "No traceback available";
  457.   },
  458.  
  459.   checkStatus: function Weave_checkStatus(code, msg, ranges) {
  460.     if (!ranges)
  461.       ranges = [[200,300]];
  462.  
  463.     for (let i = 0; i < ranges.length; i++) {
  464.       var rng = ranges[i];
  465.       if (typeof(rng) == "object" && code >= rng[0] && code < rng[1])
  466.         return true;
  467.       else if (typeof(rng) == "number" && code == rng) {
  468.         return true;
  469.       }
  470.     }
  471.  
  472.     if (msg) {
  473.       let log = Log4Moz.repository.getLogger("Service.Util");
  474.       log.error(msg + " Error code: " + code);
  475.     }
  476.  
  477.     return false;
  478.   },
  479.  
  480.   ensureStatus: function Weave_ensureStatus(args) {
  481.     if (!Utils.checkStatus.apply(Utils, arguments))
  482.       throw 'checkStatus failed';
  483.   },
  484.  
  485.   sha1: function Weave_sha1(string) {
  486.     let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
  487.       createInstance(Ci.nsIScriptableUnicodeConverter);
  488.     converter.charset = "UTF-8";
  489.  
  490.     let hasher = Cc["@mozilla.org/security/hash;1"]
  491.       .createInstance(Ci.nsICryptoHash);
  492.     hasher.init(hasher.SHA1);
  493.  
  494.     let data = converter.convertToByteArray(string, {});
  495.     hasher.update(data, data.length);
  496.     let rawHash = hasher.finish(false);
  497.  
  498.     // return the two-digit hexadecimal code for a byte
  499.     function toHexString(charCode) {
  500.       return ("0" + charCode.toString(16)).slice(-2);
  501.     }
  502.  
  503.     let hash = [toHexString(rawHash.charCodeAt(i)) for (i in rawHash)].join("");
  504.     return hash;
  505.   },
  506.  
  507.   makeURI: function Weave_makeURI(URIString) {
  508.     if (!URIString)
  509.       return null;
  510.     try {
  511.       return Svc.IO.newURI(URIString, null, null);
  512.     } catch (e) {
  513.       let log = Log4Moz.repository.getLogger("Service.Util");
  514.       log.debug("Could not create URI: " + Utils.exceptionStr(e));
  515.       return null;
  516.     }
  517.   },
  518.  
  519.   makeURL: function Weave_makeURL(URIString) {
  520.     let url = Utils.makeURI(URIString);
  521.     url.QueryInterface(Ci.nsIURL);
  522.     return url;
  523.   },
  524.  
  525.   xpath: function Weave_xpath(xmlDoc, xpathString) {
  526.     let root = xmlDoc.ownerDocument == null ?
  527.       xmlDoc.documentElement : xmlDoc.ownerDocument.documentElement;
  528.     let nsResolver = xmlDoc.createNSResolver(root);
  529.  
  530.     return xmlDoc.evaluate(xpathString, xmlDoc, nsResolver,
  531.                            Ci.nsIDOMXPathResult.ANY_TYPE, null);
  532.   },
  533.  
  534.   getTmp: function Weave_getTmp(name) {
  535.     let tmp = Svc.Directory.get("ProfD", Ci.nsIFile);
  536.     tmp.QueryInterface(Ci.nsILocalFile);
  537.  
  538.     tmp.append("weave");
  539.     tmp.append("tmp");
  540.     if (!tmp.exists())
  541.       tmp.create(tmp.DIRECTORY_TYPE, PERMS_DIRECTORY);
  542.  
  543.     if (name)
  544.       tmp.append(name);
  545.  
  546.     return tmp;
  547.   },
  548.  
  549.   /**
  550.    * Load a json object from disk
  551.    *
  552.    * @param filePath
  553.    *        Json file path load from weave/[filePath].json
  554.    * @param that
  555.    *        Object to use for logging and "this" for callback
  556.    * @param callback
  557.    *        Function to process json object as its first parameter
  558.    */
  559.   jsonLoad: function Utils_jsonLoad(filePath, that, callback) {
  560.     filePath = "weave/" + filePath + ".json";
  561.     if (that._log)
  562.       that._log.trace("Loading json from disk: " + filePath);
  563.  
  564.     let file = Utils.getProfileFile(filePath);
  565.     if (!file.exists())
  566.       return;
  567.  
  568.     try {
  569.       let [is] = Utils.open(file, "<");
  570.       let json = Utils.readStream(is);
  571.       is.close();
  572.       callback.call(that, JSON.parse(json));
  573.     }
  574.     catch (ex) {
  575.       if (that._log)
  576.         that._log.debug("Failed to load json: " + Utils.exceptionStr(ex));
  577.     }
  578.   },
  579.  
  580.   /**
  581.    * Save a json-able object to disk
  582.    *
  583.    * @param filePath
  584.    *        Json file path save to weave/[filePath].json
  585.    * @param that
  586.    *        Object to use for logging and "this" for callback
  587.    * @param callback
  588.    *        Function to provide json-able object to save. If this isn't a
  589.    *        function, it'll be used as the object to make a json string.
  590.    */
  591.   jsonSave: function Utils_jsonSave(filePath, that, callback) {
  592.     filePath = "weave/" + filePath + ".json";
  593.     if (that._log)
  594.       that._log.trace("Saving json to disk: " + filePath);
  595.  
  596.     let file = Utils.getProfileFile({ autoCreate: true, path: filePath });
  597.     let json = typeof callback == "function" ? callback.call(that) : callback;
  598.     let out = JSON.stringify(json);
  599.     let [fos] = Utils.open(file, ">");
  600.     fos.writeString(out);
  601.     fos.close();
  602.   },
  603.  
  604.   /**
  605.    * Return a timer that is scheduled to call the callback after waiting the
  606.    * provided time or as soon as possible. The timer will be set as a property
  607.    * of the provided object with the given timer name.
  608.    */
  609.   delay: function delay(callback, wait, thisObj, name) {
  610.     // Default to running right away
  611.     wait = wait || 0;
  612.  
  613.     // Use a dummy object if one wasn't provided
  614.     thisObj = thisObj || {};
  615.  
  616.     // Delay an existing timer if it exists
  617.     if (thisObj[name] instanceof Ci.nsITimer) {
  618.       thisObj[name].delay = wait;
  619.       return;
  620.     }
  621.  
  622.     // Create a special timer that we can add extra properties
  623.     let timer = {};
  624.     timer.__proto__ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  625.  
  626.     // Provide an easy way to clear out the timer
  627.     timer.clear = function() {
  628.       thisObj[name] = null;
  629.       timer.cancel();
  630.     };
  631.  
  632.     // Initialize the timer with a smart callback
  633.     timer.initWithCallback({
  634.       notify: function notify() {
  635.         // Clear out the timer once it's been triggered
  636.         timer.clear();
  637.         callback.call(thisObj, timer);
  638.       }
  639.     }, wait, timer.TYPE_ONE_SHOT);
  640.  
  641.     return thisObj[name] = timer;
  642.   },
  643.  
  644.   open: function open(pathOrFile, mode, perms) {
  645.     let stream, file;
  646.  
  647.     if (pathOrFile instanceof Ci.nsIFile) {
  648.       file = pathOrFile;
  649.     } else {
  650.       file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
  651.       dump("PATH IS" + pathOrFile + "\n");
  652.       file.initWithPath(pathOrFile);
  653.     }
  654.  
  655.     if (!perms)
  656.       perms = PERMS_FILE;
  657.  
  658.     switch(mode) {
  659.     case "<": {
  660.       if (!file.exists())
  661.         throw "Cannot open file for reading, file does not exist";
  662.       let fis = Cc["@mozilla.org/network/file-input-stream;1"].
  663.         createInstance(Ci.nsIFileInputStream);
  664.       fis.init(file, MODE_RDONLY, perms, 0);
  665.       stream = Cc["@mozilla.org/intl/converter-input-stream;1"].
  666.         createInstance(Ci.nsIConverterInputStream);
  667.       stream.init(fis, "UTF-8", 4096,
  668.                   Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
  669.     } break;
  670.  
  671.     case ">": {
  672.       let fos = Cc["@mozilla.org/network/file-output-stream;1"].
  673.         createInstance(Ci.nsIFileOutputStream);
  674.       fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, perms, 0);
  675.       stream = Cc["@mozilla.org/intl/converter-output-stream;1"]
  676.         .createInstance(Ci.nsIConverterOutputStream);
  677.       stream.init(fos, "UTF-8", 4096, 0x0000);
  678.     } break;
  679.  
  680.     case ">>": {
  681.       let fos = Cc["@mozilla.org/network/file-output-stream;1"].
  682.         createInstance(Ci.nsIFileOutputStream);
  683.       fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_APPEND, perms, 0);
  684.       stream = Cc["@mozilla.org/intl/converter-output-stream;1"]
  685.         .createInstance(Ci.nsIConverterOutputStream);
  686.       stream.init(fos, "UTF-8", 4096, 0x0000);
  687.     } break;
  688.  
  689.     default:
  690.       throw "Illegal mode to open(): " + mode;
  691.     }
  692.  
  693.     return [stream, file];
  694.   },
  695.  
  696.   /**
  697.    * Open/reshow a window/dialog based on its name and type.
  698.    *
  699.    * @param name
  700.    *        Name of the window/dialog to reshow if already open
  701.    * @param type
  702.    *        Opening behavior: "Window" or "Dialog"
  703.    * @param args
  704.    *        More arguments go here depending on the type
  705.    */
  706.   _openWin: function Utils__openWin(name, type /*, args... */) {
  707.     // Just re-show the window if it's already open
  708.     let openedWindow = Svc.WinMediator.getMostRecentWindow("Weave:" + name);
  709.     if (openedWindow) {
  710.       openedWindow.focus();
  711.       return;
  712.     }
  713.  
  714.     // Open up the window/dialog!
  715.     let win = Svc.WinWatcher;
  716.     if (type == "Dialog")
  717.       win = win.activeWindow;
  718.     win["open" + type].apply(win, Array.slice(arguments, 2));
  719.   },
  720.  
  721.   _openChromeWindow: function Utils_openCWindow(name, uri, options, args) {
  722.     Utils.openWindow(name, "chrome://weave/content/" + uri, options, args);
  723.   },
  724.  
  725.   openWindow: function Utils_openWindow(name, uri, options, args) {
  726.     Utils._openWin(name, "Window", null, uri, "",
  727.     options || "centerscreen,chrome,dialog,resizable=yes", args);
  728.   },
  729.  
  730.   openDialog: function Utils_openDialog(name, uri, options, args) {
  731.     Utils._openWin(name, "Dialog", "chrome://weave/content/" + uri, "",
  732.       options || "centerscreen,chrome,dialog,modal,resizable=no", args);
  733.   },
  734.  
  735.   openGenericDialog: function Utils_openGenericDialog(type) {
  736.     this._genericDialogType = type;
  737.     this.openDialog("ChangeSomething", "generic-change.xul");
  738.   },
  739.  
  740.   getIcon: function(iconUri, defaultIcon) {
  741.     try {
  742.       let iconURI = Utils.makeURI(iconUri);
  743.       return Svc.Favicon.getFaviconLinkForIcon(iconURI).spec;
  744.     }
  745.     catch(ex) {}
  746.  
  747.     // Just give the provided default icon or the system's default
  748.     return defaultIcon || Svc.Favicon.defaultFavicon.spec;
  749.   },
  750.  
  751.   getErrorString: function Utils_getErrorString(error, args) {
  752.     try {
  753.       return Str.errors.get(error, args || null);
  754.     } catch (e) {}
  755.  
  756.     // basically returns "Unknown Error"
  757.     return Str.errors.get("error.reason.unknown");
  758.   },
  759.  
  760.   // assumes an nsIConverterInputStream
  761.   readStream: function Weave_readStream(is) {
  762.     let ret = "", str = {};
  763.     while (is.readString(4096, str) != 0) {
  764.       ret += str.value;
  765.     }
  766.     return ret;
  767.   },
  768.  
  769.   /**
  770.    * Create an array like the first but without elements of the second
  771.    */
  772.   arraySub: function arraySub(minuend, subtrahend) {
  773.     return minuend.filter(function(i) subtrahend.indexOf(i) == -1);
  774.   },
  775.  
  776.   bind2: function Async_bind2(object, method) {
  777.     return function innerBind() { return method.apply(object, arguments); };
  778.   },
  779.  
  780.   __prefs: null,
  781.   get prefs() {
  782.     if (!this.__prefs) {
  783.       this.__prefs = Cc["@mozilla.org/preferences-service;1"]
  784.         .getService(Ci.nsIPrefService);
  785.       this.__prefs = this.__prefs.getBranch(PREFS_BRANCH);
  786.       this.__prefs.QueryInterface(Ci.nsIPrefBranch2);
  787.     }
  788.     return this.__prefs;
  789.   }
  790. };
  791.  
  792. /*
  793.  * Commonly-used services
  794.  */
  795.  
  796. let Svc = {};
  797. Svc.Prefs = new Preferences(PREFS_BRANCH);
  798. Svc.Obs = Observers;
  799. [["Annos", "@mozilla.org/browser/annotation-service;1", "nsIAnnotationService"],
  800.  ["AppInfo", "@mozilla.org/xre/app-info;1", "nsIXULAppInfo"],
  801.  ["Bookmark", "@mozilla.org/browser/nav-bookmarks-service;1", "nsINavBookmarksService"],
  802.  ["Crypto", "@labs.mozilla.com/Weave/Crypto;1", "IWeaveCrypto"],
  803.  ["Directory", "@mozilla.org/file/directory_service;1", "nsIProperties"],
  804.  ["Env", "@mozilla.org/process/environment;1", "nsIEnvironment"],
  805.  ["Favicon", "@mozilla.org/browser/favicon-service;1", "nsIFaviconService"],
  806.  ["History", "@mozilla.org/browser/nav-history-service;1", "nsPIPlacesDatabase"],
  807.  ["Idle", "@mozilla.org/widget/idleservice;1", "nsIIdleService"],
  808.  ["IO", "@mozilla.org/network/io-service;1", "nsIIOService"],
  809.  ["Login", "@mozilla.org/login-manager;1", "nsILoginManager"],
  810.  ["Memory", "@mozilla.org/xpcom/memory-service;1", "nsIMemory"],
  811.  ["Observer", "@mozilla.org/observer-service;1", "nsIObserverService"],
  812.  ["Private", "@mozilla.org/privatebrowsing;1", "nsIPrivateBrowsingService"],
  813.  ["Profiles", "@mozilla.org/toolkit/profile-service;1", "nsIToolkitProfileService"],
  814.  ["Prompt", "@mozilla.org/embedcomp/prompt-service;1", "nsIPromptService"],
  815.  ["Script", "@mozilla.org/moz/jssubscript-loader;1", "mozIJSSubScriptLoader"],
  816.  ["SysInfo", "@mozilla.org/system-info;1", "nsIPropertyBag2"],
  817.  ["Version", "@mozilla.org/xpcom/version-comparator;1", "nsIVersionComparator"],
  818.  ["WinMediator", "@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator"],
  819.  ["WinWatcher", "@mozilla.org/embedcomp/window-watcher;1", "nsIWindowWatcher"],
  820. ].forEach(function(lazy) Utils.lazySvc(Svc, lazy[0], lazy[1], Ci[lazy[2]]));
  821.  
  822. let Str = {};
  823. ["engines", "errors", "sync"]
  824.   .forEach(function(lazy) Utils.lazy2(Str, lazy, Utils.lazyStrings(lazy)));
  825.